/*
 * Created Date: Friday, October 15th 2021, 10:29:03 am
 * Author: Han
 * 作业1.1: 绘制一个兔子
 */

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <fstream>
#include <iostream>
#include <string>

#include <utils/camera.h>
#include <utils/model.h>
#include <utils/shader.h>

using namespace std;

void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow *window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow *window, int button, int action, int mods);
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

Camera *camera;

int main() {
    system("chcp 65001");
    // glfw: 初始化
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw 窗体创建
    // --------------------
    GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetScrollCallback(window, scroll_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetCursorPosCallback(window, mouse_callback);

    // glad: 加载具体平台OpenGL函数指针
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // ------------------------------------------------------------------
    // ------------------------------------------------------------------
    stbi_set_flip_vertically_on_load(true);
    // 相机
    camera = new Camera();
    camera->arcball.setSize(Vector2i(SCR_WIDTH, SCR_HEIGHT));

    // OpenGL 初始化设置
    glEnable(GL_DEPTH_TEST);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // 线框模式绘制

    // 着色器创建
    Shader *shader = new Shader();
    // shader->createFromSource(vertexShaderSource, fragmentShaderSource);
    // 在调试时，需要将 VS 工程上的 Working Directory 设为 bin 目录，才能正确识别相对路径
    shader->createFromFileName("./shader/vert.vs", "./shader/frag.fs");
    GLuint shaderProgram = shader->programId;

    // 模型加载
    // 在调试时，需要将 VS 工程上的 Working Directory 设为 bin 目录，才能正确识别相对路径
    string path = "./model/bun_zipper.ply"; 
    Model ourModel(path);

    // render loop 循环渲染
    // -----------
    while (!glfwWindowShouldClose(window)) {
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // --------------------------------------------------------------------------------
        // 1. 绑定着色器(Shader类封装好了,调用里面的API就好了)
        // 2. 设置着色器uniform变量 model view 以及 projection
        // -------------------------------------------------------------------------------
        // TODO  代码填写在这.....

        // -------------------------------------------------------------------------------

        // 绘制模型
        ourModel.Draw(shader);

        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteProgram(shaderProgram);

    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// 键盘鼠标交互 回调
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
    glViewport(0, 0, width, height);

    camera->size = {width, height};
    camera->arcball.setSize(Vector2i(width, height));
}

void mouse_callback(GLFWwindow *window, double xpos, double ypos) {
    // std::cout << xpos << std::endl;
    double x, y;
    if (!camera->is_rotate && !camera->is_translate) {
        return;
    } else if (camera->is_rotate) {
        std::cout << "旋转中..." << std::endl;
        glfwGetCursorPos(window, &x, &y);
        Eigen::Vector2i screen((int)x, (int)y);
        camera->motion_rotate(screen);
    } else if (camera->is_translate) {
        std::cout << "平移中..." << std::endl;
        glfwGetCursorPos(window, &x, &y);
        Eigen::Vector2f screen(x, y);
        camera->motion_translate(screen);
    }
}

void mouse_button_callback(GLFWwindow *window, int button, int action, int mods) {
    //左键按下
    if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
        std::cout << "左键按下..." << std::endl;
        camera->is_rotate = true;
        double x, y;
        glfwGetCursorPos(window, &x, &y);
        Eigen::Vector2i screen((int)x, (int)y);
        camera->start_rotate(screen); //开始旋转
    }
    //中键按下
    if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) {
        std::cout << "中键按下..." << std::endl;
        camera->is_translate = true;
        double x, y;
        glfwGetCursorPos(window, &x, &y);
        Eigen::Vector2f screen(x, y);
        camera->start_translate(screen); //开始平移
    }
    // 鼠标抬起
    if (action == GLFW_RELEASE) {
        std::cout << "鼠标抬起..." << std::endl;
        double x, y;
        glfwGetCursorPos(window, &x, &y);
        Eigen::Vector2i screen((int)x, (int)y);
        camera->end_rotate(screen);
        camera->end_translate(screen); //结束平移
    }
}

void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) {
    // 缩放
    camera->zoom = std::max(0.001, camera->zoom * (yoffset > 0 ? 1.1 : 0.9));
    camera->zoom = std::min(1000.0f, camera->zoom);
    std::cout << camera->zoom << std::endl;
}